package src;

/**
 * The Polynomial class, in which implements the Expression Interface
 * represents a Polynomial Expression (e.g., 1+2x+5x^2+6x^3+11x^5)
 * A polynomial expression can be evaluated, derived and return its last operation symbol.
 *
 * @author Intro111 Staff
 */

public class Polynomial implements Expression {

    private double[] coefficients;
    private Variable var;

    /**
     * Constructor that creates a polynomial expression from given coefficients and a variable
     *
     * @param var          the variable of the polynomial expression
     * @param coefficients the coefficients of every power of the variable
     */
    public Polynomial(Variable var, double[] coefficients) {
        if ((var == null) || (coefficients == null)) {
            throw new RuntimeException("Polynomial constructor got a null argument.");
        }
        this.var = var;
        this.coefficients = coefficients;
    }

    /**
     * Returns the last operation symbol of ploynomial Expression
     *
     * @return String "poly" that represents the last operation
     */
    public String getLastOperationSymbol() {
        return "poly";
    }

    /**
     * Evaluates the polynomial expression
     *
     * @return double that represents the evaluation of the polynomial expression
     */
    public double evaluate() {
        double value = 0;
        double varValue = var.getValue();
        for (int i = 0; i < coefficients.length; i = i + 1) {
            value = value + coefficients[i] * Math.pow(varValue, i);
        }
        return value;
    }


    /**
     * Derives the polynomial expression by a variable.
     *
     * @param var the variable to derive by
     * @return the derived expression by param var
     * @throws RunTimeException for a null var
     */
    public Expression derivative(Variable var) {
        if (var == null) {
            throw new RuntimeException("Polynomial derivative(Variable var) got a null argument.");
        }

        Expression derivative;
        if (this.var != var || coefficients.length == 1) {
            derivative = new Constant();
        } else {
            double[] derivativeCoefficients =
                    new double[coefficients.length - 1];
            for (int i = 1; i < coefficients.length; i = i + 1) {
                derivativeCoefficients[i - 1] = coefficients[i] * i;
            }
            derivative = new Polynomial(var, derivativeCoefficients);
        }
        return derivative;
    }

    /**
     * Returns a string representing the polynomial Expression.
     *
     * @return String that represents the polynomial Expression
     */
    public String toString() {
        String res = "(" + coefficients[0];

        if (coefficients.length > 1) {
            if (coefficients[1] < 0) res = res + coefficients[1];
            else res = res + "+" + coefficients[1];

            res = res + "*" + var.getName();
        }

        for (int i = 2; i < coefficients.length; i = i + 1) {
            if (coefficients[i] < 0) res = res + coefficients[i];
            else res = res + "+" + coefficients[i];

            res = res + "*" + var.getName() + "^" + i;
        }

        res = res + ")";
        return res;
    }

    /**
     * Compares this polynomial expression to the specified object.
     * The result is true if and only if the argument is not null, the argument is instance of polynomial expression, the variables are equals and the coefficients have the same values.
     *
     * @param other The object to compare this expression against
     * @return true if the given object represents an expression equivalent to this expression, false otherwise
     */
    public boolean equals(Object other) {
        boolean ans = false;
        if (other instanceof Polynomial) {
            Polynomial tmp = (Polynomial) other;
            if (var.equals(tmp.var))
                ans = true;
            int i;
            for (i = 0; (i < coefficients.length) && (i < tmp.coefficients.length) && ans; i++) {
                if (coefficients[i] != tmp.coefficients[i])
                    ans = false;
            }
            if (ans) {
                for (int j = i; (j < coefficients.length) && ans; j++) {
                    if (coefficients[j] != 0)
                        ans = false;
                }
                for (int j = i; (j < tmp.coefficients.length) && ans; j++) {
                    if (tmp.coefficients[j] != 0)
                        ans = false;
                }
            }
        }
        return ans;
    }
}
